The symmetry-exceptional slopes of K=t09847 are (-3,1), (-2,1), and (2,1). All three have symmetry group Z/2+Z/2 and thus they can be written in three different ways as DBC. Here we determine all these three ways. Since K is strongly invertible all slopes can be written in one way as DBC branched along a link L in S3. That means for the three symmetry-exceptional fillings we need to find another 2 ways to write them as DBCs.

In [1]:
import time
import snappy
import pandas
DBChomologies = pandas.read_csv("DBChomologies_non_alternating.csv") # We load a list of all non alternating links in 
#the HTLinkExteriors together with their homologies (if the homology is cyclic) and their crossing numbers.
### REMARK: To build the list we need to run the code in the file Build_DBChomologies which takes around 60 minutes. 
#Here we just load it.


def double_branched_cover(link):
    """
    Returns the double branched cover of the link.
    """
    L=link.copy()
    for i in range(L.num_cusps()):
        L.dehn_fill((2,0),i)
    for cov in L.covers(2):
        if (2.0, 0.0) not in cov.cusp_info('filling'):
            return cov
        


def better_is_isometric_to(X,Y,index):
    """
    Returns True if X and Y are isometric.
    Returns False if X and Y have different homologies. TO DO: Use volume to rigorously distinguish X and Y.
    Returns 'unclear' if SnapPy cannot verify it.
    The higher the index the harder SnapPy tries.
    """     
    w='unclear'
    if X.homology()!=Y.homology():
        w=False
    if w=='unclear':
        for i in (0,index):
            try:
                w=X.is_isometric_to(Y)
            except RuntimeError:
                pass
            except snappy.SnapPeaFatalError:
                pass
            if w==True:
                break
            if w==False:
                w='unclear'
            X.randomize()
            Y.randomize()
            i=i+1
    return w

def possible_DBC(homologies,max_crossings=15):
    """
    Takes a list of orders of homologies and returns a list consisting of all DBC of alternating links in the HT link table with that homologies together with the link names.
    """
    DBCList=[]
    LINKS=[]
    for order in homologies:
        LINKS=LINKS+DBChomologies.loc[(DBChomologies['homology']==order) & (DBChomologies['crossings']<=max_crossings)]['knot'].tolist()
    for link in LINKS:
        L=snappy.Manifold(link)
        D=double_branched_cover(L)
        DBCList.append([D,link])
    return DBCList

### The following two functions are written by Dunfield and search for positive triangulations.

def all_positive(manifold):
    return manifold.solution_type() == 'all tetrahedra positively oriented'

def find_positive_triangulation(manifold, tries=100):
    M = manifold.copy()
    for i in range(tries):
        if all_positive(M):
            return M
        M.randomize()
    for d in M.dual_curves():
        X = M.drill(d)
        X = X.filled_triangulation()
        X.dehn_fill((1,0))
        for i in range(tries):
            if all_positive(X):
                return X
            X.randomize()

    # In the closed case, here is another trick.
    if all(not c for c in M.cusp_info('is_complete')):
        for i in range(tries):
            # Drills out a random edge
            X = M.__class__(M.filled_triangulation())
            if all_positive(X):
                return X
            M.randomize()
            
def better_find_positive_triangulation(M,tries=1):
    '''
    Search for a positive triangulation, but ignores errors.
    '''
    RandomizeCount=0
    while RandomizeCount<tries:
        try:
            X=find_positive_triangulation(M)
            return X
        except snappy.SnapPeaFatalError:
            M.randomize()
            RandomizeCount=RandomizeCount+1
    return None

def is_alternating(knot,slope,try_hard=False,index=10,tries=1,max_cro=15):
    '''
    Checks if the slope is alternating.
    '''
    K=snappy.Manifold(knot)
    K.dehn_fill(slope)
    DBC=possible_DBC([K.homology().order()],max_crossings=max_cro) 
    for D in DBC:
        w=better_is_isometric_to(D[0],K,index)
        if w==True:
            return [slope,D[1]]
    if try_hard:
        X=better_find_positive_triangulation(K,tries)
        if X is not None:
            for D in DBC:
                Y=better_find_positive_triangulation(D[0],tries)
                if Y is not None:
                    w=better_is_isometric_to(X,Y,index)
                    if w==True:
                        return [slope,D[1]]
    return False
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
/var/folders/x0/69y_4spx515cn735q1qy2ylm0000gn/T/ipykernel_60620/2895650112.py in <module>
      1 import time
      2 import snappy
----> 3 import pandas
      4 DBChomologies = pandas.read_csv("DBChomologies_non_alternating.csv") # We load a list of all non alternating links in
      5 #the HTLinkExteriors together with their homologies (if the homology is cyclic) and their crossing numbers.

ModuleNotFoundError: No module named 'pandas'
In [2]:
K=snappy.Manifold('t09847')
exc_sym_slopes=[(-3,1),(-2,1),(2,1)]
for s in exc_sym_slopes:
    K.dehn_fill(s)
    print(s,K.symmetry_group())
(-3, 1) Z/2 + Z/2
(-2, 1) Z/2 + Z/2
(2, 1) Z/2 + Z/2
In [3]:
start_time = time.time()
branching_sets=[]
knot='t09847'
for slope in exc_sym_slopes:
    w=is_alternating(knot,slope,index=25,max_cro=15)
    if w!=False:
        branching_sets.append([knot,w[0],w[1]])
        print('We found a branching set:',knot,slope,w[1])
        
print('Total number of branching sets we have found:', len(branching_sets))
print('Total time taken: %s minutes ---' % ((time.time() - start_time)/60))
We found a branching set: t09847 (-3, 1) K15n88871
We found a branching set: t09847 (-2, 1) L14n24290
We found a branching set: t09847 (2, 1) L14n31171
Total number of branching sets we have found: 3
Total time taken: 0.5895553231239319 minutes ---

Next we will search for branching sets in more general manifolds. For that we will take links in the HT link tables fill some one component of it to get a surgery diagram of a link in a manifold. Then we take the double branched cover of that link and search for a match with a symmetric filling on A.

In [4]:
DBChomologies_branching = pandas.read_csv("DBChomologies_one_filling.csv")  
### REMARK: To build the list we need to run the code in the file Build_DBChomologies which takes around 3 days. Here we just load it.


def double_branched_cover(link):
    """
    Returns the double branched covers of the link. This works also for links in a more general manifold. 
    Note that a knot in a more general manifold may have more than one double branched cover 
    (or no double branched cover at all if the knot represents a primitive element in homology).
    This function will return the complete list of all double branched covers of the link.
    """
    L=link.copy()
    for i in range(L.num_cusps()):
        if L.cusp_info(i).filling==(0.0, 0.0):
            L.dehn_fill((2,0),i)
    return [cov for cov in L.covers(2) if (2.0, 0.0) not in cov.cusp_info('filling')]

def possible_DBC_surgery_diagrams(homologies,max_crossings=15):
    """
    Reads off the possible surgery diagrams.
    """
    DBCList=[]
    LINKS=[]
    CUSPS=[]
    SLOPES_STRINGS=[]
    for order in homologies:
        LINKS=LINKS+DBChomologies_branching.loc[(DBChomologies_branching['homology']==order) & (DBChomologies_branching['crossings']<=max_crossings)]['knot'].tolist()
        CUSPS=CUSPS+DBChomologies_branching.loc[(DBChomologies_branching['homology']==order) & (DBChomologies_branching['crossings']<=max_crossings)]['cusp'].tolist()
        SLOPES_STRINGS=SLOPES_STRINGS+DBChomologies_branching.loc[(DBChomologies_branching['homology']==order) & (DBChomologies_branching['crossings']<=max_crossings)]['filling'].tolist()
    SLOPES=[]
    for string in SLOPES_STRINGS:
        string_without_brackets=string[1:-1]
        SLOPES.append(tuple(map(int, string_without_brackets.split(', '))))
    for i in range(0,len(LINKS)):
        DBCList.append([LINKS[i],SLOPES[i],CUSPS[i]])
    return DBCList

def search_for_branching_set(knot,slope,try_hard=False,index=10,tries=1,max_crossings=15):
    '''
    Searchs for a surgery diagram of the rbanching set
    '''  
    K=snappy.Manifold(knot)
    K.dehn_fill(slope)
    DBC=possible_DBC_surgery_diagrams([K.homology().order()],max_crossings) 
    for DIAGRAM in DBC:
        L=snappy.Manifold(DIAGRAM[0])
        L.dehn_fill(DIAGRAM[1],DIAGRAM[2])
        for D in double_branched_cover(L):
            w=better_is_isometric_to(D,K,index)
            if w==True:
                return [[knot,slope,DIAGRAM[0],DIAGRAM[1],DIAGRAM[2]]]
    if try_hard:
        X=better_find_positive_triangulation(K,tries)
        if X is not None:
            for DIAGRAM in DBC:
                L=snappy.Manifold(DIAGRAM[0])
                L.dehn_fill(DIAGRAM[1],DIAGRAM[2])
                for D in double_branched_cover(L):
                    Y=better_find_positive_triangulation(D,tries)
                    if Y is not None:
                        w=better_is_isometric_to(X,Y,index)
                        if w==True:
                            return [[knot,slope,DIAGRAM[0],DIAGRAM[1],DIAGRAM[2]]]                                  
    return False

def search_for_three_branching_sets(knot,slope,try_hard=False,index=10,tries=1,max_crossings=15):
    '''
    Searchs for a surgery diagram of the branching set
    '''  
    BRANCHING_SETS=[]
    K=snappy.Manifold(knot)
    K.dehn_fill(slope)
    homologies=[]
    DBC=possible_DBC_surgery_diagrams([K.homology().order()],max_crossings) 
    for DIAGRAM in DBC:
        L=snappy.Manifold(DIAGRAM[0])
        L.dehn_fill(DIAGRAM[1],DIAGRAM[2])
        for D in double_branched_cover(L):
            w=better_is_isometric_to(D,K,index)
            if w==True:
                BRANCHING_SETS=BRANCHING_SETS+[[knot,slope,DIAGRAM[0],DIAGRAM[1],DIAGRAM[2]]]
                for i in range(L.num_cusps()):
                    if L.cusp_info(i).filling==(0.0, 0.0):
                        L.dehn_fill((1,0),i)
                if L.homology().order() not in homologies:
                    homologies.append(L.homology().order())
                if len(homologies)==3:
                    return BRANCHING_SETS
    if try_hard:
        X=better_find_positive_triangulation(K,tries)
        if X is not None:
            for DIAGRAM in DBC:
                L=snappy.Manifold(DIAGRAM[0])
                L.dehn_fill(DIAGRAM[1],DIAGRAM[2])
                for D in double_branched_cover(L):
                    Y=better_find_positive_triangulation(D,tries)
                    if Y is not None:
                        w=better_is_isometric_to(X,Y,index)
                        if w==True:
                            BRANCHING_SETS=BRANCHING_SETS+[[knot,slope,DIAGRAM[0],DIAGRAM[1],DIAGRAM[2]]]
                            for i in range(L.num_cusps()):
                                if L.cusp_info(i).filling==(0.0, 0.0):
                                    L.dehn_fill((1,0),i)
                            if L.homology().order() not in homologies:
                                homologies.append(L.homology().order())
                            if len(homologies)==3:
                                return BRANCHING_SETS
    return BRANCHING_SETS
In [5]:
start_time = time.time()
knot='t09847'
for slope in exc_sym_slopes:
    w=search_for_three_branching_sets(knot,slope,index=3,max_crossings=15)
    if w==[]:
        w=False
    if w!=False:
        branching_sets=branching_sets+w
        print('We found a branching set:',w)
        
print('Total number of branching sets we have found:', len(branching_sets))
print('Total time taken: %s minutes ---' % ((time.time() - start_time)/60))
We found a branching set: [['t09847', (-3, 1), 'L13n5917', (-3, 1), 0], ['t09847', (-3, 1), 'L14n24778', (-3, 1), 0]]
We found a branching set: [['t09847', (-2, 1), 'L11n420', (5, 3), 0], ['t09847', (-2, 1), 'L13n7290', (-2, 1), 1], ['t09847', (-2, 1), 'L13n9355', (-5, 2), 1], ['t09847', (-2, 1), 'L13n9861', (2, 1), 1]]
We found a branching set: [['t09847', (2, 1), 'L12n1082', (-2, 1), 0]]
Total number of branching sets we have found: 10
Total time taken: 112.62691159645716 minutes ---
In [6]:
branching_sets
Out[6]:
[['t09847', (-3, 1), 'K15n88871'],
 ['t09847', (-2, 1), 'L14n24290'],
 ['t09847', (2, 1), 'L14n31171'],
 ['t09847', (-3, 1), 'L13n5917', (-3, 1), 0],
 ['t09847', (-3, 1), 'L14n24778', (-3, 1), 0],
 ['t09847', (-2, 1), 'L11n420', (5, 3), 0],
 ['t09847', (-2, 1), 'L13n7290', (-2, 1), 1],
 ['t09847', (-2, 1), 'L13n9355', (-5, 2), 1],
 ['t09847', (-2, 1), 'L13n9861', (2, 1), 1],
 ['t09847', (2, 1), 'L12n1082', (-2, 1), 0]]

Since the knot K is strongly invertible we have always at least on way to write the knot as a DBC over a link in S3. The first 3 branching sets in S3 are these generic branching sets. The other surgery descriptions yield surgery descriptions in lens spaces. We still need to find one more branching set for (-3,1) and one more for (2,1). For that we search for surgery descriptions where we fill two surgery curves.

In [3]:
K16=snappy.Manifold('t09847(2,1)')
K16
Out[3]:
t09847(2,1)
In [4]:
K16.homology()
Out[4]:
Z/16
In [5]:
K21=snappy.Manifold('t09847(-3,1)')
K21
Out[5]:
t09847(-3,1)
In [6]:
K21.homology()
Out[6]:
Z/21
In [7]:
def better_is_isometric_to(X,Y,index=10,return_isometries=False):
    """
    Returns True if X and Y are isometric.
    Returns False if it is unclear.
    """     
    w=False
    for i in (0,index):
        try:
            w=X.is_isometric_to(Y,return_isometries)
            return w
        except (RuntimeError, snappy.SnapPeaFatalError):
            X.randomize()
            Y.randomize()
            i=i+1
    return w

def gcd(a, b):
    while b != 0:
        a, b = b, a % b
    return a

def coprime(a, b):
    return gcd(a, b) == 1

slope_set=[]
for q in range (1,5):
    for p in range (2,11):
        if coprime(p,q)==True:
            slope_set.append((p,q))
            slope_set.append((-p,q))
            
            
def double_branched_cover(link):
    """
    Returns the double branched covers of the link. This works also for links in a more general manifold. 
    Note that a knot in a more general manifold may have more than one double branched cover 
    (or no double branched cover at all if the knot represents a primitive element in homology).
    This function will return the complete list of all double branched covers of the link.
    """
    L=link.copy()
    for i in range(L.num_cusps()):
        if L.cusp_info(i).filling==(0.0, 0.0):
            L.dehn_fill((2,0),i)
    return [cov for cov in L.covers(2) if (2.0, 0.0) not in cov.cusp_info('filling')]
In [18]:
start_time = time.time()


for L in snappy.HTLinkExteriors(knots_vs_links='links'):
    if L.num_cusps()==3:
        for r in slope_set:
            for s in slope_set:
                L.dehn_fill([r,s,(0,0)])
                DBC=double_branched_cover(L)
                for D in DBC:
                    hom=D.homology()
                    if len(hom)==1:
                        if D.homology().coefficients==[16]:
                            if better_is_isometric_to(D,K16):
                                print(L)
                        if D.homology().coefficients==[21]:
                            if better_is_isometric_to(D,K21):
                                print(L)
                L.dehn_fill([r,(0,0),s])
                DBC=double_branched_cover(L)
                for D in DBC:
                    hom=D.homology()
                    if len(hom)==1:
                        if D.homology().coefficients==[16]:
                            if better_is_isometric_to(D,K16):
                                print(L)
                        if D.homology().coefficients==[21]:
                            if better_is_isometric_to(D,K21):
                                print(L)
                L.dehn_fill([(0,0),r,s])
                DBC=double_branched_cover(L)
                for D in DBC:
                    hom=D.homology()
                    if len(hom)==1:
                        if D.homology().coefficients==[16]:
                            if better_is_isometric_to(D,K16):
                                print(L)
                        if D.homology().coefficients==[21]:
                            if better_is_isometric_to(D,K21):
                                print(L)
 
                       

print("--- Time taken: %s hours ---" % (time.time() - start_time)/3600)  
L10n92(0,0)(-7,4)(-8,3)
L11n294(-2,3)(-5,1)(0,0)
L11n420(0,0)(7,3)(4,3)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
/var/folders/x0/69y_4spx515cn735q1qy2ylm0000gn/T/ipykernel_58315/2258601642.py in <module>
     16                         if D.homology().coefficients==[Integer(21)]:
     17                             if better_is_isometric_to(D,K21):
---> 18                                 print(L)
     19                 L.dehn_fill([r,(Integer(0),Integer(0)),s])
     20                 DBC=double_branched_cover(L)

cython/core/triangulation.pyx in SnapPy.Triangulation.__repr__()

cython/core/manifold.pyx in SnapPy.Manifold.cusp_info()

~/.sage/local/lib/python3.10/site-packages/snappy/__init__.py in <lambda>(n)
    121 from .sage_helper import _within_sage
    122 if _within_sage:
--> 123     to_sage = lambda n : n.sage()
    124     Manifold.use_field_conversion(to_sage)
    125     ManifoldHP.use_field_conversion(to_sage)

~/.sage/local/lib/python3.10/site-packages/snappy/number.py in sage(self)
    534             return RealField(self._precision)(self.gen)
    535         elif self.gen.type() == 't_COMPLEX':
--> 536             return ComplexField(self._precision)(self.gen)
    537         else:
    538             return self.gen.sage()

/private/var/tmp/sage-9.6-current/local/var/lib/sage/venv-python3.10.3/lib/python3.10/site-packages/sage/rings/complex_mpfr.pyx in sage.rings.complex_mpfr.ComplexField_class.__call__ (build/cythonized/sage/rings/complex_mpfr.c:7336)()
    482         if im is not None:
    483             x = x, im
--> 484         return Parent.__call__(self, x)
    485 
    486     def _element_constructor_(self, x):

/private/var/tmp/sage-9.6-current/local/var/lib/sage/venv-python3.10.3/lib/python3.10/site-packages/sage/structure/parent.pyx in sage.structure.parent.Parent.__call__ (build/cythonized/sage/structure/parent.c:9450)()
    895         if mor is not None:
    896             if no_extra_args:
--> 897                 return mor._call_(x)
    898             else:
    899                 return mor._call_with_args(x, args, kwds)

/private/var/tmp/sage-9.6-current/local/var/lib/sage/venv-python3.10.3/lib/python3.10/site-packages/sage/structure/coerce_maps.pyx in sage.structure.coerce_maps.DefaultConvertMap_unique._call_ (build/cythonized/sage/structure/coerce_maps.c:4734)()
    159                 print(type(C), C)
    160                 print(type(C._element_constructor), C._element_constructor)
--> 161             raise
    162 
    163     cpdef Element _call_with_args(self, x, args=(), kwds={}):

/private/var/tmp/sage-9.6-current/local/var/lib/sage/venv-python3.10.3/lib/python3.10/site-packages/sage/structure/coerce_maps.pyx in sage.structure.coerce_maps.DefaultConvertMap_unique._call_ (build/cythonized/sage/structure/coerce_maps.c:4626)()
    154         cdef Parent C = self._codomain
    155         try:
--> 156             return C._element_constructor(x)
    157         except Exception:
    158             if print_warnings:

/private/var/tmp/sage-9.6-current/local/var/lib/sage/venv-python3.10.3/lib/python3.10/site-packages/sage/rings/complex_mpfr.pyx in sage.rings.complex_mpfr.ComplexField_class._element_constructor_ (build/cythonized/sage/rings/complex_mpfr.c:8251)()
    533 
    534             try:
--> 535                 return self(x.sage())
    536             except (AttributeError, TypeError):
    537                 pass

cypari2/gen.pyx in cypari2.gen.Gen.sage()

/private/var/tmp/sage-9.6-current/local/var/lib/sage/venv-python3.10.3/lib/python3.10/site-packages/sage/libs/pari/convert_sage.pyx in sage.libs.pari.convert_sage.gen_to_sage (build/cythonized/sage/libs/pari/convert_sage.c:4800)()
     39 
     40 
---> 41 cpdef gen_to_sage(Gen z, locals=None):
     42     """
     43     Convert a PARI gen to a Sage/Python object.

/private/var/tmp/sage-9.6-current/local/var/lib/sage/venv-python3.10.3/lib/python3.10/site-packages/sage/libs/pari/convert_sage.pyx in sage.libs.pari.convert_sage.gen_to_sage (build/cythonized/sage/libs/pari/convert_sage.c:4034)()
    296         else:
    297             K = QuadraticField(-1, 'i')
--> 298             return K([gen_to_sage(real), gen_to_sage(imag)])
    299     elif t == t_VEC or t == t_COL:
    300         return [gen_to_sage(x, locals) for x in z.python_list()]

/private/var/tmp/sage-9.6-current/local/var/lib/sage/venv-python3.10.3/lib/python3.10/site-packages/sage/libs/pari/convert_sage.pyx in sage.libs.pari.convert_sage.gen_to_sage (build/cythonized/sage/libs/pari/convert_sage.c:4666)()
    324     from sage.misc.sage_eval import sage_eval
    325     locals = {} if locals is None else locals
--> 326     return sage_eval(str(z), locals=locals)
    327 
    328 

/private/var/tmp/sage-9.6-current/local/var/lib/sage/venv-python3.10.3/lib/python3.10/site-packages/sage/misc/sage_eval.py in sage_eval(source, locals, cmds, preparse)
    196         return locals['_sage_eval_returnval_']
    197     else:
--> 198         return eval(source, sage.all.__dict__, locals)
    199 
    200 

/private/var/tmp/sage-9.6-current/local/var/lib/sage/venv-python3.10.3/lib/python3.10/site-packages/sage/all.py in <module>

NameError: name 'nan' is not defined

The first two surgery descriptions yield the two different manifold. And we can check via KLO that the yield links in different lens spaces.

In [9]:
B21=snappy.Manifold('L11n294(-2,3)(-5,1)(0,0)')
D=double_branched_cover(B21)[0]
D.homology()
Out[9]:
Z/21
In [11]:
D.is_isometric_to(K21)
Out[11]:
True
In [12]:
B16=snappy.Manifold('L10n92(0,0)(-7,4)(-8,3)')
D=double_branched_cover(B16)[0]
D.homology()
Out[12]:
Z/16
In [13]:
D.is_isometric_to(K16)
Out[13]:
True